
use aho_corasick::AhoCorasick;
use clickhouse_rs::types::{Decimal, SqlType};
use once_cell::sync::Lazy;
use crate::prelude::{get_app_config, get_ch_pool};
static AHO_MAP: Lazy<dashmap::DashMap<String, AhoCorasick>> = Lazy::new(|| dashmap::DashMap::new());
/// 解析sql,替换参数
fn sql_parse(sqls: &str, params_names: &[&str], params_vals: &[&str]) -> anyhow::Result<String> {
    let name = params_names.join("|");
    if !AHO_MAP.contains_key(&name) {
        let ac = AhoCorasick::builder().build(params_names).unwrap();
        AHO_MAP.insert(name.clone(), ac);
    }
    let ac = AHO_MAP.get(&name).unwrap();
    let mut wtr = vec![];
    ac.try_stream_replace_all(sqls.as_bytes(), &mut wtr, params_vals)
        .expect("字符串替换失败");
    let sql = String::from_utf8(wtr)?;
    Ok(sql)
}
pub async fn exec_sqls(
    sqls: &str,
    params_names: &[&str],
    params_vals: &[&str],
) -> anyhow::Result<()> {
    let sqls= sql_parse(sqls, params_names, params_vals)?;
    let vec: Vec<&str> = sqls.split(';').collect();
    let mut client = get_ch_pool().get_handle().await?;
    for sql in vec {
        let sql=sql.trim();
        if sql.is_empty() {
            continue;
        }
        client.execute(sql).await?;
    }
    Ok(())
}
pub async fn table_is_exist(table_name:&str) -> anyhow::Result<bool> {
    let mut client = get_ch_pool().get_handle().await?;
    let db_name = get_app_config().clickhouse.database.as_str();
    let sql_str = format!(
        "select count(*) from system.tables where database='{}' and name='{}'",
        db_name,
        table_name
    );
    let block = client.query(sql_str).fetch_all().await?;
    let mut count = 0u64;
    if let Some(row) = block.rows().next() {
        count = row.get(0)?
    }
    Ok(count > 0)
}
pub async fn get_columns(table_name: &str) -> anyhow::Result<Vec<(String, SqlType)>> {
    let mut client = get_ch_pool().get_handle().await?;
    let sql_str = format!("select top 1 * from {}", table_name);
    let block = client.query(sql_str).fetch_all().await?;
    let cols = block.columns();
    let cols: Vec<(String, SqlType)> = cols
        .iter()
        .map(|col| (col.name().to_string(), col.sql_type().clone()))
        .collect();
    Ok(cols)
}
pub fn str_to_decmial(val:&str,p:u8,s:u8)->anyhow::Result<Decimal>{
    let v = if val.is_empty() {
        Decimal::default()
    } else {
        let vals: Vec<&str> = val.trim_end_matches('0').split('.').collect();
        let vlen = vals.len();
        if vlen > 2 {
            return Err(anyhow::anyhow!("值{}不是有效的Decimal(p={},s={})", val, p, s));
        }
        for v in vals.iter() {
            if !v.bytes().enumerate().all(|(i, c)| {
                if i == 0 {
                    c == b'-' || c.is_ascii_digit()
                } else {
                    c.is_ascii_digit()
                }
            }) {
                return Err(anyhow::anyhow!("值{}不是有效的Decimal(p={},s={})", val, p, s));
            }
        }
        let (v2, p2, s2) = if vlen == 2 {
            let s2 = vals[1].len() as u8;
            if s2 > s {
                let sidx=s as usize;
                let fv:f64=format!("{}.{}",&vals[1][0..sidx],&vals[1][sidx..]).parse()?;
                let v = format!("{}{:.0}", vals[0], fv.round());
                let p2 = vals[0].len() as u8 + s;
                (v, p2, s)
            }else{
                let p2 = vals[0].len() as u8 + s2;
                let v = format!("{}{}", vals[0], vals[1]);
                (v, p2, s2)
            }
        } else {
            let p2 = vals[0].len() as u8;
            let v: String = vals[0].to_string();
            (v, p2, 0)
        };
        if p2 > p || s2 > s {
            return Err(anyhow::anyhow!("值{}的精度超过了Decimal(p={},s={})", val, p, s));
        }
        if v2.is_empty() {
            Decimal::default()
        }else{
            let dv: i64 = v2.parse()?;
            Decimal::new(dv, s2)
        }
    };
    Ok(v)
}
